package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strings"
	"time"

	_ "github.com/go-sql-driver/mysql"
)

type GnDiff struct {
	F1           int     `json:"f1"`   // 2
	Price        float64 `json:"f2"`   // 板块价格
	Zdf          float64 `json:"f3"`   // 涨跌幅百分比
	Code         string  `json:"f12"`  // 板块代码
	F13          int     `json:"f13"`  // 90
	BkName       string  `json:"f14"`  // 板块名
	Zljlre       float64 `json:"f62"`  // 今日主力净流入-净额
	Cddlre       float64 `json:"f66"`  // 今日超大单净流入-净额
	Cddlrzb      float64 `json:"f69"`  // 今日超大单净流入-占比
	Ddlre        float64 `json:"f72"`  // 今日大单净流入-净额
	Ddlrzb       float64 `json:"f75"`  // 今日大单净流入-占比
	Zdlre        float64 `json:"f78"`  // 今日中单净流入-净额
	Zdlrzb       float64 `json:"f81"`  // 今日中单净流入-占比
	Xdlre        float64 `json:"f84"`  // 今日小单净流入-净额
	Xdlrzb       float64 `json:"f87"`  // 今日小单净流入-占比
	DataTime     int     `json:"f124"` // 时间戳
	Zljlrzb      float64 `json:"f184"` // 今日主力净流入-占比
	BigstockName string  `json:"f204"` // 今日主力净流入最大股
	BigstockCode string  `json:"f205"` // 今日主力净流入最大股股票代码（重复）
	F206         int     `json:"f206"` // 1
}

//

type Gnbk struct {
	Data struct {
		Total int      `json:"total"`
		Diff  []GnDiff `json:"diff"`
	} `json:"data"`
}

// fetchData 获取数据并处理JSONP响应
func fetchData(url string) ([]byte, []byte, error) {
	referer := "https://data.eastmoney.com/bkzj/gn.html"

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		fmt.Println("创建请求失败:", url, nil)
		return nil, nil, err
	}
	req.Header.Add("Referer", referer)
	req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
	resp, err := client.Do(req)
	fmt.Println(url)
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, err
	}

	// 处理JSONP响应：提取JSON部分
	re := regexp.MustCompile(`\w+\(([\s\S]*)\);?$`)
	matches := re.FindStringSubmatch(string(body))

	var jsonData string
	if len(matches) > 1 {
		jsonData = matches[1]
	} else {
		jsonData = string(body)
	}
	// fmt.Println(string(jsonData))

	return []byte(jsonData), body, nil
}

func buildInsertSQL(tableName string, columns, updateColumns []string) string {

	// 构造 INSERT 部分
	placeholder := strings.Repeat("?, ", len(columns)-1) + "?"
	insertSQL := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", tableName, strings.Join(columns, ", "), placeholder)

	// 构造 ON DUPLICATE KEY UPDATE 部分
	updateExpr := make([]string, len(updateColumns))
	for i, col := range updateColumns {
		updateExpr[i] = fmt.Sprintf("%s = VALUES(%s)", col, col)
	}
	finalSQL := fmt.Sprintf("%s ON DUPLICATE KEY UPDATE %s", insertSQL, strings.Join(updateExpr, ", "))

	return finalSQL
}

func main() {
	db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/stock?parseTime=true")
	if err != nil {
		fmt.Println("无法打开数据库:", err)
		return
	}
	defer db.Close()

	err = db.Ping()
	if err != nil {
		fmt.Println("无法连接到数据库:", err)
		return
	}

	currentDate := time.Now().Format("20060102")

	GnbkURL := "https://push2.eastmoney.com/api/qt/clist/get?cb=jQuery112306007533199735811_%d&fid=f3&po=1&pz=50&pn=1&np=1&fltt=2&invt=2&ut=8dec03ba335b81bf4ebdf7b29ec27d15&fs=m%%3A90+t%%3A3&fields=f12%%2Cf14%%2Cf2%%2Cf3%%2Cf62%%2Cf184%%2Cf66%%2Cf69%%2Cf72%%2Cf75%%2Cf78%%2Cf81%%2Cf84%%2Cf87%%2Cf204%%2Cf205%%2Cf124%%2Cf1%%2Cf13"
	timestamp := time.Now().UnixMilli()
	GnbkURL = fmt.Sprintf(GnbkURL, timestamp)
	gnbkData, respBody, err := fetchData(GnbkURL)

	if err != nil {
		fmt.Printf("概念板块数据获取失败: %v\n", err)
	} else {
		var gnbk Gnbk
		if err := json.Unmarshal(gnbkData, &gnbk); err != nil {
			fmt.Printf("概念板块数据解析失败: %v\n", err)
			fmt.Printf("原始响应前200字符: %s\n", string(respBody)[:200])
		} else {
			UpdateColumns := []string{
				"f1", "price", "zdf", "f13", "bkname", //
				"zljlre", "cddlre", "cddlrzb", "ddlre", "ddlrzb", //
				"zdlre", "zdlrzb", "xdlre", "xdlrzb", "datatime", //
				"zljlrzb", "bigstock_name", "bigstock_code", "f206",
			}
			Colums := append(UpdateColumns, "qdate", "code")

			prepareSQL := buildInsertSQL("gnbktop", Colums, UpdateColumns)
			stmt, err := db.Prepare(prepareSQL)
			if err != nil {
				fmt.Println("准备语句失败:", err)
				return
			}
			defer stmt.Close()

			for _, v := range gnbk.Data.Diff {
				_, err := stmt.Exec(v.F1, v.Price, v.Zdf, v.F13, v.BkName, //
					v.Zljlre, v.Cddlre, v.Cddlrzb, v.Ddlre, v.Ddlrzb, v.Zdlre, //
					v.Zdlrzb, v.Xdlre, v.Xdlrzb, v.DataTime, v.Zljlrzb, //
					v.BigstockName, v.BigstockCode, v.F206, currentDate, v.Code)
				if err != nil {
					fmt.Printf("执行插入或更新语句失败: %v\n", err)
				}
			}
			fmt.Printf("概念板块数据获取成功: %d只股票\n", len(gnbk.Data.Diff))
		}
	}
}
